
# include "dfxTexture_OGL.h"
# include "dfxGraphicsDevice_OGL.h"
# include "dfxLog.h"

namespace DFX
{
    Texture_OGL::Texture_OGL(const String &name, GraphicsDevice *gd, TEXTURE_TYPE type, UInt32 usage, bool mipMaps)
        : Texture(name, gd, type, usage, mipMaps), mHandle(0), mTarget(0)
    {
    }

    Texture_OGL::~Texture_OGL()
    {
        Destroy();
    }

    DFXRESULT Texture_OGL::Load2D(Image *img)
    {
        if (mHandle)
            Destroy();
        
        mTarget = GraphicsDevice_OGL::TextureType[mType];

        glGenTextures(1, &mHandle);
        glBindTexture(mTarget, mHandle);

        while (glGetError() != GL_NO_ERROR);

        glTexImage2D(mTarget, 0, GraphicsDevice_OGL::ImageFormat[img->GetFormat()], img->GetWidth(), 
            img->GetHeight(), 0, GraphicsDevice_OGL::ImageFormat[img->GetFormat()], 
            GL_UNSIGNED_BYTE, img->GetData());

        mWidth = img->GetWidth();
        mHeight = img->GetHeight();        

        GLenum err = glGetError();

        if (err != GL_NO_ERROR)
        {
            DFX_ERROR("Error loading data into texture %s", mName.c_str());
            return DFX_LOADFAILED;
        }

        if (mMipMaps && (mUsage & TEXTURE_USAGE_MIPMAP))
            glGenerateMipmap(mTarget);

        return DFX_SUCCESS;
    }

    DFXRESULT Texture_OGL::LoadCube(const ImageArray &images)
    {
        if (mHandle)
            Destroy();

        mTarget = GraphicsDevice_OGL::TextureType[mType];

        glGenTextures(1, &mHandle);
        glBindTexture(mTarget, mHandle);

        while (glGetError() != GL_NO_ERROR);

        for (GLuint i = 0; i < 6; i++)
        {
            GLuint target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;

            glTexImage2D(mTarget, 0, GraphicsDevice_OGL::ImageFormat[images[i]->GetFormat()], 
                images[i]->GetWidth(), images[i]->GetHeight(), 0, GraphicsDevice_OGL::ImageFormat[images[i]->GetFormat()], 
                GL_UNSIGNED_BYTE, images[i]->GetData());
        }

        mWidth = images[0]->GetWidth();
        mHeight = images[0]->GetHeight();

        if (glGetError() != GL_NO_ERROR)
            return DFX_LOADFAILED;

        if (mMipMaps && (mUsage & TEXTURE_USAGE_MIPMAP))
            glGenerateMipmap(mTarget);

        return DFX_SUCCESS;
    }

    DFXRESULT Texture_OGL::Create(SURFACE_FORMAT format, UInt32 width, UInt32 height, UInt32 depth)
    {
        if (mHandle)
            Destroy();

        mTarget = GraphicsDevice_OGL::TextureType[mType];

        glGenTextures(1, (GLuint*)&mHandle);
        glBindTexture(mTarget, mHandle);

        while (glGetError() != GL_NO_ERROR);

        if (mTarget == GL_TEXTURE_1D)
        {
            glTexImage1D(mTarget, 0, GraphicsDevice_OGL::SurfaceFormat[format], width, 0,
                GL_RGB, GL_UNSIGNED_BYTE, NULL);
        }
        else if (mTarget == GL_TEXTURE_2D)
        {
            glTexImage2D(mTarget, 0, GraphicsDevice_OGL::SurfaceFormat[format], width, height, 0,
                GL_RGB, GL_UNSIGNED_BYTE, NULL);
        }
        else if (mTarget == GL_TEXTURE_3D)
        {
            glTexImage3D(mTarget, 0, GraphicsDevice_OGL::SurfaceFormat[format], width, height, depth, 0,
                GL_RGB, GL_UNSIGNED_BYTE, NULL);
        }
        else
        {
            DFX_ERROR("ZIOTextureOGL::Create() - unknown texture type");
            glDeleteTextures(1, (GLuint*)&mHandle);

            return DFX_CREATEFAILED;
        }

        if (glGetError() != GL_NO_ERROR)
            return DFX_CREATEFAILED;

        return DFX_SUCCESS;
    }

    void Texture_OGL::SetFilter(TEXTURE_FILTER filter)
    {
        if (mFilter == filter)
            return;

        mFilter = filter;
        glBindTexture(mTarget, mHandle);

        if (mUsage & TEXTURE_USAGE_MIPMAP)
        {
            switch (filter)
            {
            case TEXTURE_FILTER_NEAREST:
                {
                    glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
                    glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                }
                break;

            case TEXTURE_FILTER_LINEAR:
                {
                    glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
                    glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                }
                break;

            case TEXTURE_FILTER_BILINEAR:
                {
                    glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
                    glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                }
                break;

            case TEXTURE_FILTER_TRILINEAR:
                {
                    glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
                    glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                }
                break;

            default:
                {
                    glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
                    glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                }
                break;
            }
        }
        else
        {
            switch (filter)
            {
            case TEXTURE_FILTER_NEAREST:
                {
                     glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                     glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                }
                break;

            default:
                {
                     glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                     glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                }
                break;
            }
        }
    }

    void Texture_OGL::SetAnisotropy(Float32 anisotropy)
    {
    }

    void Texture_OGL::SetWrapS(TEXTURE_WRAP wrapS)
    {
        if (mWrapS == wrapS)
            return;

        glBindTexture(mTarget, mHandle);
        glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, GraphicsDevice_OGL::TextureWrap[wrapS]);
        
        mWrapS = wrapS;
    }

    void Texture_OGL::SetWrapT(TEXTURE_WRAP wrapT)
    {
        if (mWrapT == wrapT)
            return;

        glBindTexture(mTarget, mHandle);
        glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, GraphicsDevice_OGL::TextureWrap[wrapT]);

        mWrapT = wrapT;
    }

    void Texture_OGL::SetWrapR(TEXTURE_WRAP wrapR)
    {
        if (mWrapR == wrapR)
            return;

        glBindTexture(mTarget, mHandle);
        glTexParameteri(mTarget, GL_TEXTURE_WRAP_R, GraphicsDevice_OGL::TextureWrap[wrapR]);

        mWrapR = wrapR;
    }

    DFXRESULT Texture_OGL::Destroy()
    {
        if (mHandle)
            glDeleteTextures(1, (GLuint*)&mHandle);

        mHandle = 0;

        return DFX_SUCCESS;
    }
}
